# Note: This version uses tkinter instead of PyQt5 for environments where PyQt5 is not available
import sys
import threading
import time
import serial
import serial.tools.list_ports
import tkinter as tk
from tkinter import filedialog, ttk, messagebox

class SerialBurnerUI(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("串口烧录工具")
        self.geometry("700x400")

        self.ser = None
        self.is_burning = False
        self.file_path = None

        self.create_widgets()
        self.refresh_ports()

    def create_widgets(self):
        frame = tk.Frame(self)
        frame.pack(pady=10, padx=10, fill='x')

        tk.Label(frame, text="串口号：").pack(side='left')
        self.port_combo = ttk.Combobox(frame, state="readonly")
        self.port_combo.pack(side='left', padx=5)

        self.file_label = tk.Label(frame, text="未选择文件", width=40, anchor='w')
        self.file_label.pack(side='left', padx=5)

        tk.Button(frame, text="选择文件", command=self.select_file).pack(side='left', padx=5)
        self.open_button = tk.Button(frame, text="打开串口", command=self.toggle_serial)
        self.open_button.pack(side='left', padx=5)
        self.burn_button = tk.Button(frame, text="开始烧录", command=self.start_burn)
        self.burn_button.pack(side='left', padx=5)

        self.log_output = tk.Text(self, height=15)
        self.log_output.pack(fill='both', padx=10, pady=5, expand=True)

        self.progress = ttk.Progressbar(self, orient='horizontal', length=600, mode='determinate')
        self.progress.pack(pady=5)

    def refresh_ports(self):
        ports = list(serial.tools.list_ports.comports())
        port_list = [port.device for port in ports]
        self.port_combo['values'] = port_list
        if port_list:
            self.port_combo.current(0)

    def select_file(self):
        path = filedialog.askopenfilename(filetypes=[("Binary Files", "*.bin"), ("All Files", "*")])
        if path:
            self.file_path = path
            self.file_label.config(text=f"{path}")

    def toggle_serial(self):
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.ser = None
            self.log_output.insert('end', "[串口] 已关闭\n")
            self.open_button.config(text="打开串口")
        else:
            port = self.port_combo.get()
            try:
                self.ser = serial.Serial(port, 115200, timeout=1)
                self.log_output.insert('end', f"[串口] 已打开 {port}\n")
                self.open_button.config(text="关闭串口")
            except Exception as e:
                messagebox.showerror("串口错误", str(e))

    def start_burn(self):
        if not self.ser or not self.ser.is_open:
            messagebox.showwarning("警告", "请先打开串口！")
            return

        if not self.file_path:
            messagebox.showwarning("警告", "请先选择烧录文件！")
            return

        self.burn_button.config(state='disabled')
        self.progress['value'] = 0
        self.log_output.insert('end', "[烧录] 开始烧录...\n")
        self.is_burning = True

        threading.Thread(target=self.burn_thread, daemon=True).start()

    def burn_thread(self):
        try:
            with open(self.file_path, 'rb') as f:
                data = f.read()
                total_len = len(data)
                chunk_size = 64

                for i in range(0, total_len, chunk_size):
                    if not self.is_burning:
                        break
                    chunk = data[i:i+chunk_size]
                    self.ser.write(chunk)
                    self.ser.flush()
                    time.sleep(0.05)
                    percent = int((i + chunk_size) / total_len * 100)
                    self.progress['value'] = min(percent, 100)
                    self.log_output.insert('end', f"[烧录] 已发送 {i+chunk_size} / {total_len} 字节\n")
                    self.log_output.see('end')

                self.log_output.insert('end', "[烧录] 完成\n")
        except Exception as e:
            self.log_output.insert('end', f"[错误] {str(e)}\n")
        finally:
            self.burn_button.config(state='normal')
            self.is_burning = False

if __name__ == '__main__':
    app = SerialBurnerUI()
    app.mainloop()
